package loaderlib.loader
{
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;
	
	import loaderlib.progress.IProgressReporter;

	/**
	 * <code>It</code> is a template class. 
	 * 
	 * i hope it work fine. Header define a queue's header, which the queue named linkName.
	 * for example if a XXheader set the linkName="AA". it means the XXheader own a attribute AA that link to a request.
	 * and the request has the attribute AA that link the request as a queue. 
	 * As such structure, we can link the same request in difference queue very simple. just define the linkName and
	 * add special code for special header. 
	 * 
	 * Header's forEach and every function is very importance. for special header, the two function can solve most job.
	 * 
	 * @author TangZhen
	 * 
	 */
	public class Header extends EventDispatcher implements IProgressReporter
	{
		
		/**
		 * the linkName is the attribute name in the Request.
		 * <p>
		 * for most suitable usage. the Request should have this attribute.
		 * but if it haven't, the code can work, becouse the Request is dynamic class type.
		 * </p>
		 * 
		 * @default null
		 */
		protected var linkName:String = null;
		
		protected var relation:String = null;
		
		
		/**
		 * The requestQueue is the link, that connect as a queue.
		 * 
		 * <p>The requestLink should has a Request append it. if there are no Request, it means it just cached.
		 * So if need free the space, the null requestLink is choosen.</p>
		 * 
		 * @default null 
		 */
		public var requestQueue:Request;
		
		public function Header()
		{
			super(null);
		}
		
		/**
		 * <code>every</code> function travel thought the request queue for testting.
		 * 
		 * <span><p>it start on the startRequest, travel the queue and call the callback function in each queue element for testting
		 * </p>
		 * <p><strong>Notice: </strong>the callback function has such proto <strong>callback(request:Request):Boolean</strong>.</p>
		 * </span>
		 * 
		 * @param callback the callback function 
		 * @param startRequest the travel start request, default is null, means the travel start from begining. 
		 * @param thisObject the callback function's parameter "this object"
		 * @return A value of <code>true</code> means no element apply the callback return false. 
		 * <code>false</code> means any element apply the callback return true.
		 * 
		 */
		internal function every(callback:Function,startRequest:Request = null,thisObject:*=null):Boolean
		{
			var request:Request;
			if(startRequest)
			{
				request = startRequest
			}
			else
			{
				request = this.requestQueue;
			}
			if(!request)
			{
				return true;
			}
			do{
/**
*	hope the callback apply work fine. 
*/
				if(!callback.apply(thisObject?thisObject:this,[request]))
				return false;
			}
			while(request=request.hasOwnProperty(this.linkName)?request[this.linkName]:null)
			return true;
		}
		
		/**
		 * <code>forEach</code> travel thought the request queue for setting.
		 * 
		 * <span><p>it start on the startRequest, travel the queue and call the callback function in each queue element for setting</p>
		 * 
		 * <p><strong>Notice:</strong> the callback function has such proto <strong>callback(request:Request):Boolean</strong></p>
		 * </span>
		 * 
		 * @param callback the set function
		 * @param startRequest the travel start request, default is null, means the travel start from  the begining.
		 * @param thisObject the callback function "this object"
		 */
		internal function forEach(callback:Function,startRequest:Request = null,thisObject:*=null):void
		{
			var request:Request;
			if(startRequest)
			{
				request = startRequest
			}
			else
			{
				request = this.requestQueue;
			}
			if(!request)
			{
				return;
			}
			do{
/**
*	hope the callback apply work fine. 
*/
				callback.apply(thisObject?thisObject:this,[request])
			}
			while(request=request.hasOwnProperty(this.linkName)?request[this.linkName]:null)
		}
		
		/**
		 * appendRequest append the request on the queue
		 * 
		 * @param request
		 * @return ture: if add it successful. false: if the queue has the request yet. 
		 * 
		 */
		internal function appendRequest(request:Request):Boolean
		{
			var lastRequest:Request=null;
			var diffFunction:Function = function(testRequest:Request):Boolean
			{
				if(testRequest.equal(request))
					return false;
				lastRequest = testRequest;
				return true;
			}
			if(!this.every(diffFunction))
			{
				return false;
			}
			if(lastRequest == null)
			{	
				this.requestQueue = request		
			}
			else
			{
				lastRequest[this.linkName] = request			
			}
			return true
		}
		
		/**
		 * deleteRequest delete the request on the queue
		 * 
		 * @param request
		 * @return A value of <code>ture</code> means it delete the request successful;
		 * <code>false</code> means the queue hasn't the request, so it did nothing.
		 * 
		 */
		internal function deleteRequest(request:Request):Boolean
		{
			var lastRequest:Request=null;
			var diffFunction:Function = function(testRequest:Request):Boolean
			{
				if(testRequest.equal(request))
					return false;
				lastRequest = testRequest;
				return true;
			}
			if(this.every(diffFunction))
			{
				return false;
			}
			if(lastRequest == null)
			{	
				this.requestQueue = this.requestQueue[this.linkName]	
			}
			else
			{
				lastRequest[this.linkName] = lastRequest[this.linkName][this.linkName]			
			}
			return true
		}
		
		/**
		 * hasRequest function search the request queue 
		 *
		 * @param request
		 * @return a request: if find the request queue on the queue. null: if there are no such request on the queue.
		 * 
		 */
		internal function hasRequest(request:Request):Request
		{
			var returnRequest:Request=null;
			var testFunction:Function = function(testRequest:Request):Boolean
			{
				if(testRequest.equal(request))
				{
					returnRequest = testRequest
					return false;
				}
				return true;
			}
			if(!this.every(testFunction))
			{
				return returnRequest;
			}
			return null;		
		}
		
		/**
		 * this getter get the number on the queue
		 * 
		 * @return the number on the queue
		 * 
		 */
		public function get queueNum():int
		{
			var number:int=0;
			var countFunction:Function = function(testRequest:Request):void
			{
				number++;
			}
			this.forEach(countFunction);	
			return number
		}
		
		/**
		 * this getter get whether the request queue is empty
		 * 
		 * @return whether the requestQueue is null 
		 * 
		 */
		public function get emptyQueue():Boolean
		{
			if(this.requestQueue)
			{
				return true
			}
			else
			{
				return false
			}
		}
		
		/**
		 * bytesLoaded getter get loaded bytes.
		 * 
		 * this function should be overrided.
		 * 
		 * @return Number of loaded bytes
		 * 
		 */
		public function get bytesLoaded():Number
		{
			return 0
		}
		
		/**
		 * bytesTotal getter get demand bytes
		 * 
		 * this function should be overrided
		 * 
		 * @return Number of demand bytes
		 * 
		 */
		public function get bytesTotal():Number
		{
			return 0
		}
		
		/**
		 * info getter get loading information
		 * 
		 * this function should be overrided
		 * 
		 * @return info formated as XML
		 * 
		 */
		public function get info():XML
		{
			return null
		}
	}
}